import logging
import hmac
import hashlib
import time
import base64
import json
import asyncio
import threading
from datetime import datetime
from typing import List, Dict, Any, Optional, Callable, Union
from websocket import create_connection, WebSocketApp
from ..interface import IExchange, ExchangeType, KLineInterval
from ..utils import HttpClient, HttpError
from ..models import KLineData
from utils.time_utils import convert_to_beijing_time

logger = logging.getLogger(__name__)

class GateAdapter(IExchange):
    """Gate.io交易所适配器"""
    
    # API基础URL
    BASE_URL = "https://api.gateio.ws/api/v4"
    # WebSocket基础URL
    WS_BASE_URL = "wss://fx-ws.gateio.ws/v4/ws"
    WS_TEST_URL = "wss://fx-ws-testnet.gateio.ws/v4/ws"
    
    # K线间隔映射
    INTERVAL_MAP = {
        KLineInterval.MINUTE_1: '1m',
        KLineInterval.MINUTE_5: '5m',
        KLineInterval.MINUTE_15: '15m',
        KLineInterval.MINUTE_30: '30m',
        KLineInterval.HOUR_1: '1h',
        KLineInterval.HOUR_4: '4h',
        KLineInterval.HOUR_6: '6h',
        KLineInterval.HOUR_12: '12h',
        KLineInterval.DAY_1: '1d',
        KLineInterval.WEEK_1: '7d',  # Gate.io使用7d表示一周
        KLineInterval.MONTH_1: '30d',  # Gate.io使用30d表示一个月
    }
    
    # 订单时效性类型
    TIF_TYPES = {
        'gtc': '一直有效直至取消',
        'ioc': '立即成交或取消',
        'poc': '被动成交或取消',
        'fok': '完全成交或取消'
    }
    
    def __init__(self, api_key: str = "", api_secret: str = "", use_testnet: bool = False):
        """
        初始化Gate.io交易所适配器
        
        参数:
            api_key: API密钥
            api_secret: API密钥秘钥
            use_testnet: 是否使用测试网络
        """
        self.api_key = api_key
        self.api_secret = api_secret
        self.http_client = HttpClient(base_url=self.BASE_URL)
        self.use_testnet = use_testnet
        self.ws_url = self.WS_TEST_URL if use_testnet else self.WS_BASE_URL
        self.ws_connections = {}  # 存储WebSocket连接
        self.ws_ready = {}        # 存储WebSocket连接就绪状态
        self.ws_callbacks = {}    # 存储回调函数
        self.ws_running = False   # WebSocket连接状态
        self.pending_subscriptions = {} # 存储待发送的订阅请求
    
    def get_name(self) -> str:
        """获取交易所名称"""
        return "Gate.io"
    
    def get_exchange_type(self) -> ExchangeType:
        """获取交易所类型"""
        return ExchangeType.GATE
    
    def _generate_signature(self, method: str, url: str, query_string: str, body: str, timestamp: int) -> str:
        """
        生成API签名
        
        参数:
            method: 请求方法
            url: 请求路径
            query_string: 查询字符串
            body: 请求体
            timestamp: 时间戳
            
        返回:
            签名字符串
        """
        # 先对查询字符串和请求体进行SHA512哈希
        m = hashlib.sha512()
        m.update((query_string + body).encode('utf-8'))
        hashed_payload = m.hexdigest()
        
        # 构建签名字符串
        message = f"{method}\n{url}\n{query_string}\n{hashed_payload}\n{timestamp}"
        
        # 使用HMAC-SHA512计算签名
        signature = hmac.new(
            self.api_secret.encode('utf-8'),
            message.encode('utf-8'),
            hashlib.sha512
        ).hexdigest()
        
        return signature
    
    def _get_auth_headers(self, method: str, url: str, query_string: str = "", body: str = "") -> Dict[str, str]:
        """
        获取带认证的请求头
        
        参数:
            method: 请求方法
            url: 请求路径
            query_string: 查询字符串
            body: 请求体
            
        返回:
            请求头字典
        """
        if not self.api_key or not self.api_secret:
            return {}
            
        timestamp = int(time.time())
        signature = self._generate_signature(method, "/api/v4" + url, query_string, body, timestamp)
        
        return {
            "KEY": self.api_key,
            "Timestamp": str(timestamp),
            "SIGN": signature,
            "Content-Type": "application/json"
        }
    
    async def get_klines(
        self, 
        symbol: str, 
        interval: KLineInterval, 
        start_time: Optional[datetime] = None, 
        end_time: Optional[datetime] = None,
        limit: Optional[int] = None
    ) -> List[Dict[str, Any]]:
        """
        获取K线数据
        
        参数:
            symbol: 交易对符号
            interval: K线时间间隔
            start_time: 开始时间（可选）
            end_time: 结束时间（可选）
            limit: 返回的K线数量限制（可选）
            
        返回:
            K线数据列表
        """
        # 转换交易对格式（Gate.io使用BTC_USDT格式）
        symbol = symbol.replace('-', '_').upper()
        
        # 转换时间间隔
        interval_str = self.INTERVAL_MAP.get(interval)
        if not interval_str:
            raise ValueError(f"不支持的时间间隔: {interval}")
        
        # 默认使用usdt结算
        settle = 'usdt'
        
        # 构建请求参数
        params = {
            'contract': symbol,
            'interval': interval_str,
        }
        
        # 添加可选参数
        if start_time:
            params['from'] = int(start_time.timestamp())
        if end_time:
            params['to'] = int(end_time.timestamp())
        if limit:
            params['limit'] = limit
        
        try:
            # 发送请求 - 改为合约接口
            endpoint = f"/futures/{settle}/candlesticks"
            response = await self.http_client.request(
                method="GET",
                url=endpoint,
                params=params
            )
            
            # 转换响应数据
            result = []
            for item in response:
                # Gate.io 合约K线数据格式:
                # {
                #   "t": 1530626880,  // 时间戳
                #   "v": 100,         // 成交量 (张)
                #   "c": "9000",      // 收盘价
                #   "h": "9000",      // 最高价
                #   "l": "9000",      // 最低价
                #   "o": "9000"       // 开盘价
                # }
                
                timestamp = int(item['t'])
                # 使用utcfromtimestamp直接获取UTC时间
                datetime_utc = datetime.utcfromtimestamp(timestamp)
                # 将UTC时间转换为北京时间
                beijing_time = convert_to_beijing_time(datetime_utc)
                
                amount = float(item.get('v', 0)) # 成交额 (以USDT计价)
                close_price = float(item['c'])
                # 成交量 (以币为单位)
                volume = amount / close_price if close_price > 0 else 0
                
                kline_data = {
                    'symbol': symbol.replace('_', '-'),
                    'datetime': beijing_time,
                    'datetime_utc': datetime_utc,
                    'open': float(item['o']),
                    'high': float(item['h']),
                    'low': float(item['l']),
                    'close': close_price,
                    'volume': volume,
                    'amount': amount
                }
                result.append(kline_data)
                
            return result
            
        except HttpError as e:
            logger.error(f"获取K线数据失败: {e}")
            raise
    
    async def get_symbols(self) -> List[str]:
        """
        获取所有可用的交易对符号
        
        返回:
            交易对符号列表
        """
        # 默认使用usdt结算
        settle = 'usdt'
        
        try:
            # 改为合约接口
            endpoint = f"/futures/{settle}/contracts"
            response = await self.http_client.request(
                method="GET",
                url=endpoint
            )
            
            # 转换交易对格式
            symbols = [item['name'].replace('_', '-') for item in response if not item.get('in_delisting')]
            return symbols
            
        except HttpError as e:
            logger.error(f"获取交易对列表失败: {e}")
            raise
    
    async def get_ticker(self, symbol: str) -> Dict[str, Any]:
        """
        获取交易对的最新行情
        
        参数:
            symbol: 交易对符号
            
        返回:
            包含最新行情的字典
        """
        # 转换交易对格式
        gate_symbol = symbol.replace('-', '_').upper()
        # 默认使用usdt结算
        settle = 'usdt'
        
        try:
            # 改为合约接口
            endpoint = f"/futures/{settle}/tickers"
            params = {'contract': gate_symbol}
            response = await self.http_client.request(
                method="GET",
                url=endpoint,
                params=params
            )
            
            if not response:
                raise ValueError(f"未找到交易对: {symbol}")
                
            ticker_data = response[0]
            
            # 转换响应数据
            result = {
                'symbol': symbol,
                'last': float(ticker_data['last']),
                'high_24h': float(ticker_data['high_24h']),
                'low_24h': float(ticker_data['low_24h']),
                'volume_24h': float(ticker_data.get('volume_24h', 0)), # 成交量(张)
                'amount_24h': float(ticker_data.get('volume_24h_usd', 0)) # 成交额(USDT)
            }
            return result
            
        except HttpError as e:
            logger.error(f"获取 {symbol} 行情失败: {e}")
            raise
    
    async def create_order(self, 
                           symbol: str, 
                           size: int, 
                           price: str, 
                           side: str = "buy", 
                           order_type: str = "limit", 
                           text: str = "", 
                           auto_size : str = "",
                           reduce_only: bool = False,
                           close: bool = False,
                           iceberg: int = 0) -> Dict[str, Any]:
        """
        创建合约订单
        
        参数:
            symbol: 交易对符号，例如 BTC-USDT
            size: 订单数量，正数为买入，负数为卖出
            price: 订单价格，字符串类型
            side: 订单方向，buy或sell，如果size已经包含符号则此参数可忽略
            order_type: 订单类型，limit(限价单)或market(市价单)
            text: 订单自定义信息，长度不超过30个字符，以t-开头的将被视为用户自定义ID
            reduce_only: 是否只减仓，不增仓
            close: 是否平仓单
            iceberg: 冰山单显示数量，0表示非冰山单
            
        返回:
            订单信息字典
        """
        if not self.api_key or not self.api_secret:
            raise ValueError("API密钥和密钥秘钥必须设置才能下单")
        
        # 转换交易对格式（Gate.io使用BTC_USDT格式）
        gate_symbol = symbol.replace('-', '_').upper()
        
        # 默认使用usdt结算
        settle = 'usdt'
        
        # 调整订单数量的符号（正数为买入，负数为卖出）
        if side.lower() == "sell" and size > 0:
            size = -size
        elif side.lower() == "buy" and size < 0:
            size = abs(size)
        
        # 构建请求体
        order_data = {
            "contract": gate_symbol,
            "size": size,
            "price": price,
            "tif": "ioc"
        }
        
        # 添加可选参数
        if text:
            order_data["text"] = text
        if reduce_only:
            order_data["reduce_only"] = reduce_only
        if close:
            order_data["close"] = close
        if auto_size != '':
            order_data["auto_size"] = auto_size
        if iceberg > 0:
            order_data["iceberg"] = iceberg
        
        # 如果是市价单，可以不传价格
        if order_type.lower() == "market":
            order_data["price"] = 0
        
        # 序列化请求体为JSON字符串
        body = json.dumps(order_data)
        try:
            # 构造请求路径
            endpoint = f"/futures/{settle}/orders"
            url = endpoint
            
            # 获取认证头
            auth_headers = self._get_auth_headers("POST", url, "", body)
            
            logger.info(f"下单请求体: {body}, endpoint: {endpoint}, headers: {auth_headers}")
            # 发送请求
            response = await self.http_client.request(
                method="POST",
                url=endpoint,
                data=body,
                headers=auth_headers
            )
            logger.info(f"下单响应: {response}")
            # 返回订单信息
            return response
            
        except HttpError as e:
            logger.error(f"创建订单失败: {e}")
            raise
    
    async def close(self):
        """关闭HTTP客户端和WebSocket连接"""
        await self.http_client.close()
        self.close_all_ws_connections()
    
    def close_all_ws_connections(self):
        """关闭所有WebSocket连接"""
        for settle, ws in self.ws_connections.items():
            if ws:
                logger.info(f"关闭 {settle} WebSocket连接")
                ws.close()
        self.ws_connections = {}
        self.ws_running = False

    def send_ping(self):
        """
        发送ping消息以保持WebSocket连接活跃
        """
        try:
            # 遍历所有活跃的WebSocket连接发送ping
            for settle, ws in self.ws_connections.items():
                if ws and self.ws_ready.get(settle, False):
                    # 构建ping消息
                    ping_message = {
                        'time': int(time.time()),
                        'channel': 'futures.ping',
                        'event': 'ping'
                    }
                    # 发送ping消息
                    ws.send(json.dumps(ping_message))
                    logger.debug(f"已发送ping消息到 {settle} WebSocket")
        except Exception as e:
            logger.error(f"发送ping消息失败: {e}")

    def _get_ws_url(self, settle: str) -> str:
        """
        获取WebSocket URL
        
        参数:
            settle: 结算货币（例如：btc, usdt）
            
        返回:
            WebSocket URL
        """
        base_url = self.WS_TEST_URL if self.use_testnet else self.WS_BASE_URL
        return f"{base_url}/{settle.lower()}"
    
    def _on_ws_message(self, ws, message: str):
        """
        WebSocket消息回调处理函数
        
        参数:
            ws: WebSocket连接
            message: 接收到的消息
        """
        try:
            data = json.loads(message)
            logger.debug(f"WebSocket收到消息: {data}")
            
            # 处理pong响应
            if data.get('event') == 'pong':
                logger.debug(f"收到pong响应: {data}")
                return
            
            # 处理订阅结果消息
            if data.get('event') == 'subscribe':
                channel = data.get('channel')
                if data.get('result', {}).get('status') == 'success':
                    logger.info(f"成功订阅频道: {channel}")
                else:
                    logger.error(f"订阅频道失败: {channel}, 原因: {data}")
                return
            
            # 处理更新消息
            if data.get('event') == 'update':
                channel = data.get('channel')
                result = data.get('result', [])
                
                # 调用相应频道的回调函数处理更新数据
                callbacks = self.ws_callbacks.get(channel, [])
                for callback in callbacks:
                    try:
                        callback(result)
                    except Exception as e:
                        logger.error(f"执行回调函数异常: {e}")
        except json.JSONDecodeError:
            logger.error(f"解析WebSocket消息失败: {message}")
        except Exception as e:
            logger.error(f"处理WebSocket消息异常: {e}")
    
    def _on_ws_error(self, ws, error):
        """
        WebSocket错误回调处理函数
        
        参数:
            ws: WebSocket连接
            error: 错误信息
        """
        logger.error(f"WebSocket错误: {error}")
    
    def _on_ws_close(self, ws, close_status_code, close_reason):
        """
        WebSocket关闭回调处理函数
        
        参数:
            ws: WebSocket连接
            close_status_code: 关闭状态码
            close_reason: 关闭原因
        """
        logger.info(f"WebSocket连接关闭: {close_status_code}, {close_reason}")
        # 可以在这里添加重连逻辑
    
    def _on_ws_open(self, ws):
        """
        WebSocket打开回调处理函数
        
        参数:
            ws: WebSocket连接
        """
        logger.info("WebSocket连接已打开")
        # 找到对应的settle
        for settle, conn in self.ws_connections.items():
            if conn == ws:
                logger.info(f"标记 {settle} WebSocket连接为就绪状态")
                self.ws_ready[settle] = True
                
                # 发送所有待处理的订阅请求
                if settle in self.pending_subscriptions:
                    for subscription in self.pending_subscriptions[settle]:
                        try:
                            ws.send(json.dumps(subscription))
                            logger.info(f"已发送延迟的订阅请求: {subscription}")
                        except Exception as e:
                            logger.error(f"发送延迟的订阅请求失败: {e}")
                    # 清空待处理队列
                    self.pending_subscriptions[settle] = []
                break
    
    def _create_ws_connection(self, settle: str) -> WebSocketApp:
        """
        创建WebSocket连接
        
        参数:
            settle: 结算货币（例如：btc, usdt）
            
        返回:
            WebSocketApp实例
        """
        ws_url = self._get_ws_url(settle)
        logger.info(f"创建WebSocket连接: {ws_url}")
        
        # 初始化连接状态和订阅队列
        self.ws_ready[settle] = False
        self.pending_subscriptions[settle] = []
        
        ws = WebSocketApp(
            ws_url,
            on_open=self._on_ws_open,
            on_message=self._on_ws_message,
            on_error=self._on_ws_error,
            on_close=self._on_ws_close
        )
        
        # 在新线程中启动WebSocket连接
        def run_websocket():
            while self.ws_running:
                try:
                    ws.run_forever()
                    if self.ws_running:
                        logger.info(f"WebSocket连接断开，5秒后重连...")
                        # 重置连接状态
                        self.ws_ready[settle] = False
                        time.sleep(5)
                except Exception as e:
                    logger.error(f"WebSocket连接异常: {e}")
                    # 重置连接状态
                    self.ws_ready[settle] = False
                    if self.ws_running:
                        logger.info(f"5秒后重连...")
                        time.sleep(5)
        
        self.ws_running = True
        thread = threading.Thread(target=run_websocket)
        thread.daemon = True
        thread.start()
        
        return ws
    
    def _get_or_create_ws_connection(self, settle: str) -> WebSocketApp:
        """
        获取或创建WebSocket连接
        
        参数:
            settle: 结算货币（例如：btc, usdt）
            
        返回:
            WebSocketApp实例
        """
        settle = settle.lower()
        if settle not in self.ws_connections or self.ws_connections[settle] is None:
            self.ws_connections[settle] = self._create_ws_connection(settle)
        return self.ws_connections[settle]
    
    def subscribe_ticker(self, symbol: str, callback: Callable[[List[Dict[str, Any]]], None]) -> bool:
        """
        订阅交易对的ticker数据
        
        参数:
            symbol: 交易对符号（例如：BTC-USDT）
            callback: 回调函数，接收ticker数据列表
            
        返回:
            订阅是否成功
        """
        # 转换交易对格式（Gate.io使用BTC_USDT格式）
        gate_symbol = symbol.replace('-', '_').upper()
        
        # 默认使用usdt结算
        settle = 'usdt'
        
        # 获取WebSocket连接
        ws = self._get_or_create_ws_connection(settle)
        
        # 构建订阅消息
        channel = 'futures.tickers'
        subscription = {
            'time': int(time.time()),
            'channel': channel,
            'event': 'subscribe',
            'payload': [gate_symbol]
        }
        
        # 添加回调函数
        if channel not in self.ws_callbacks:
            self.ws_callbacks[channel] = []
        self.ws_callbacks[channel].append(callback)
        
        # 发送订阅消息或加入待处理队列
        try:
            if self.ws_ready.get(settle, False):
                # 连接已就绪，直接发送
                ws.send(json.dumps(subscription))
                logger.info(f"已发送ticker订阅请求: {gate_symbol}")
                return True
            else:
                # 连接未就绪，加入待处理队列
                logger.info(f"WebSocket连接未就绪，将ticker订阅请求加入队列: {gate_symbol}")
                self.pending_subscriptions[settle].append(subscription)
                return True
        except Exception as e:
            logger.error(f"发送ticker订阅请求失败: {e}")
            return False
    
    def subscribe_kline(self, symbol: str, interval: Union[KLineInterval, str], callback: Callable[[List[Dict[str, Any]]], None]) -> bool:
        """
        订阅交易对的K线数据
        
        参数:
            symbol: 交易对符号（例如：BTC-USDT）
            interval: K线时间间隔
            callback: 回调函数，接收K线数据列表
            
        返回:
            订阅是否成功
        """
        # 转换交易对格式（Gate.io使用BTC_USDT格式）
        gate_symbol = symbol.replace('-', '_').upper()
        
        # 转换时间间隔
        if isinstance(interval, KLineInterval):
            interval_str = self.INTERVAL_MAP.get(interval)
            if not interval_str:
                raise ValueError(f"不支持的时间间隔: {interval}")
        else:
            interval_str = interval
        
        # 默认使用usdt结算
        settle = 'usdt'
        
        # 获取WebSocket连接
        ws = self._get_or_create_ws_connection(settle)
        
        # 构建订阅消息
        channel = 'futures.candlesticks'
        subscription = {
            'time': int(time.time()),
            'channel': channel,
            'event': 'subscribe',
            'payload': [interval_str, gate_symbol]
        }
        
        # 添加回调函数
        if channel not in self.ws_callbacks:
            self.ws_callbacks[channel] = []
        self.ws_callbacks[channel].append(callback)
        
        # 发送订阅消息或加入待处理队列
        try:
            if self.ws_ready.get(settle, False):
                # 连接已就绪，直接发送
                ws.send(json.dumps(subscription))
                logger.info(f"已发送K线订阅请求: {gate_symbol}, 间隔: {interval_str}")
                return True
            else:
                # 连接未就绪，加入待处理队列
                logger.info(f"WebSocket连接未就绪，将K线订阅请求加入队列: {gate_symbol}, 间隔: {interval_str}")
                self.pending_subscriptions[settle].append(subscription)
                return True
        except Exception as e:
            logger.error(f"发送K线订阅请求失败: {e}")
            return False 